#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include "mqtt/mqtt.h"
#include "JSON/cJSON.h"
#include "PCM/pcm.h"
#include "avenc.h"
#include "ts/Mux.h"
#include "rtsp/rtsp.h"
#include "rtp/rtp.h"
#include "CRtmpPush.h"
//#include "audiodec.h"
#include "MQTTProcess.h"
#include "Common.h"
#include "globaldata.h"

#include "config.h"

#include "hi_include/mpi_venc.h"
#include "hi_include/hi_comm_video.h"
#include "hi_include/mpi_vb.h"
#include "hi_include/mpi_sys.h"
#include "hi_include/hi_math.h"
#include "hi_include/mpi_venc.h"
#include "hi_include/mpi_vpss.h"
#include "hi3516ev200_gpio.h"

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/videoio.hpp>

#include "rtsp_server.h"

#include "wifimgr.h"

static int writetsdata(unsigned char *data, int len)
{
	//printf("send rtp pack\n");
	mrtp->sendPack((unsigned char *)data, len);
}

static int rtmp_write_a(unsigned char *buf, int len, int pts)
{
	static unsigned int ainc = 0;
	pts = 0;
	pts = ainc++ * (1000 * 1024 / 44100);
	getBandwidth(len);
	//printf("audio pts %d\n",pts);
	rtmp.writeAACRawData(buf, len, pts);
	return 0;
}
static int rtmp_write_v(unsigned char *buf, int len, int pts)
{
	static unsigned int vinc = 0;
	getBandwidth(len);
	pts = 0;
	pts = vinc++ * (1000 / 25);
	//printf("video pts %d\n",pts);
	rtmp.writeH264RawData(buf, len, pts);
	return 0;
}
static int rtpts_write_a(unsigned char *buf, int len, int pts)
{
	//AUDIO_SAMPLE_RATE_24000
	//rtmp.writeAACRawData(buf,len,pts);
	//RUN_TEST;

	getBandwidth(len);
	WriteBuf2TsFile(&ts_session, 44100, pts, 0, buf, len, 0);
	return 0;
}

static int rtpts_write_v(unsigned char *buf, int len, int pts)
{
	//printf("################\n");
	//RUN_TEST;
	getBandwidth(len);
	WriteBuf2TsFile(&ts_session, 25, pts, 1, buf, len, 0);
	return 0;
}

static int all_write_v(unsigned char *buf, int len, int pts)
{
	return 0;
}
static int all_write_a(unsigned char *buf, int len, int pts)
{
	return 0;
}

static int rtsp_write_v(unsigned char *buf, int len, int pts)
{
	rtspSrv->rtspsendFrame(buf, len);
	return 0;
}
static int rtsp_write_a(unsigned char *buf, int len, int pts)
{
	return 0;
}

HI_S32 GetFrameBlkInfo(VIDEO_FRAME_INFO_S *stVideoFrameInfo);

#define NETWORK_DEFAULT 0
#define NETWORK_WIFIAP 1  //WIFIAP
#define NETWORK_WIFISTA 2 //连接wifi路由器
#define NETWORK_4GLTE 3	  //4g网络

#define MEDIA_RTSP 0
#define MEDIA_RTMP 1
#define MEDIA_AVCHAT 2

static char rtmpurl[256];
static char wifissid[32];
static char wifipass[32];
static char rtphost[32];
static char rtpport[32];
static unsigned networkmod = NETWORK_WIFIAP;
static unsigned mediamod = MEDIA_RTSP;
struct MEDIA_WRITE mwrtmp;

static int readInifile()
{
#define CONFIG_INIT_FILE_PATH "/SDCARD/iotcamcfg.ini"

	char __tmp[32];
	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "NETWORKMOD", __tmp);
	sscanf(__tmp, "%d", &networkmod);

	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "MEDIAMOD", __tmp);
	sscanf(__tmp, "%d", &mediamod);

	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "RTMPURL", rtmpurl);

	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "RTPPORT", rtpport);
	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "RTPHOST", rtphost);

	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "WIFISSID", wifissid);
	GetProfileString(CONFIG_INIT_FILE_PATH, "GENERAL", "WIFIPASS", wifipass);

	printf("NETWORKMOD %d\n", networkmod);
	printf("MEDIAMOD %d\n", mediamod);

	printf("WIFISSID %s\n", wifissid);
	printf("WIFIPASS %s\n", wifipass);

	printf("RTMPURL %s\n", rtmpurl);

	printf("RTPHOST %s\n", rtphost);
	printf("RTPPORT %s\n", rtpport);
}
void get_runtime_initvalue()
{ //初始化runtime参数，填写默认值，或者从文件读取
	rt_data.alarm_flag = 0;
	rt_data.audio_enable = 1;
	rt_data.video_enable = 1;
	rt_data.light_enable = 0;
	rt_data.record_enable = 0;
	rt_data.resolution = 1;
	rt_data.frame_rate = 30;
	rt_data.bit_rate = 100;
	rt_data.video_quality = 50;
	rt_data.sample_rate = 5;
}
void initrtmp()
{
	mwrtmp.write_a = rtmp_write_a;
	mwrtmp.write_v = rtmp_write_v;
	mwrtmp.chn = 1; //使用通道1的560p码流
	register_mw(&mwrtmp);
	av_start();

	rtmp.start();
	//	pause_write(1);
	//	rtmp.startPush();
}
void startrtmp(char *url)
{
	rtmp.setUrl(url);
	pause_write(1);
	rtmp.startPush();
	pause_write(0);
}
void stoprtmp()
{
	pause_write(1);
	rtmp.stopPush();
	//	pause_write(0);
}
int getmsgcode(char *msg)
{
	cJSON *pJson;
	cJSON *pSub;
	if (NULL == msg)
	{
		return 0;
	}
	pJson = cJSON_Parse(msg);
	if (NULL == pJson)
	{
		printf("pJson is NULL\r\n");
		return 0;
	}

	pSub = cJSON_GetObjectItem(pJson, "MSGCODE");

	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
		return -1;
	}
	else
	{
		printf("%s %d MSGCODE=:%d\n", __FILE__, __LINE__, pSub->valueint);
		return pSub->valueint;
	}
	cJSON_Delete(pJson);
}
int heartbeat_rtn_process(char *msg)
{

	printf("%s %d \r\n", __FILE__, __LINE__);
	char url[1024] = {0};
	int stream, storage, audio_enable, video_enable, resolution, record_enable, light_eanble, frame_rate, bitrate, quality, sample_rate;

	cJSON *pJson;
	cJSON *pSub;
	if (NULL == msg)
	{
		return 0;
	}
	pJson = cJSON_Parse(msg);
	if (NULL == pJson)
	{
		printf("pJson is NULL\r\n");
		return 0;
	}
	pSub = cJSON_GetObjectItem(pJson, "URL");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		sprintf(url, "%s", pSub->valuestring);
		printf("url:%s\n", url);
	}

	pSub = cJSON_GetObjectItem(pJson, "STREAM");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		stream = pSub->valueint;
		printf("STREAM = :%d\n", stream);
	}

	pSub = cJSON_GetObjectItem(pJson, "STORAGE");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		storage = pSub->valueint;
		printf("STORAGE = :%d\n", storage);
	}

	pSub = cJSON_GetObjectItem(pJson, "AUDIO_ENABLE");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		audio_enable = pSub->valueint;
		printf("AUDIO_ENABLE = :%d\n", audio_enable);
	}

	pSub = cJSON_GetObjectItem(pJson, "VIDEO_ENABLE");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		audio_enable = pSub->valueint;
		printf("VIDEO_ENABLE = :%d\n", video_enable);
	}

	pSub = cJSON_GetObjectItem(pJson, "RESOLUTION");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		resolution = pSub->valueint;
		printf("VIDEO_ENABLE = :%d\n", resolution);
	}

	pSub = cJSON_GetObjectItem(pJson, "RECORD_ENABLE");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		record_enable = pSub->valueint;
		printf("RECORD_ENABLE = :%d\n", record_enable);
	}

		pSub = cJSON_GetObjectItem(pJson, "LIGHT_ENABLE");
	if (pSub == NULL)
	{
		printf("pSub is NULL\r\n");
	}
	else
	{
		light_eanble = pSub->valueint;
		printf("LIGHT_ENABLE = :%d\n", light_eanble);
	}

	cJSON_Delete(pJson);

	int state = rt_data.stream_state;
	switch (state)
	{
	case STREAM_STATE_STOP: //stopping
		if (stream == STREAM_STATE_RTMP)
		{ //应该先释放一次资源
			//stoprtmp();
			printf("start rtmp\r\n");
			startrtmp(url);
			//rt_data.stream_state=1;
		}
		else if (stream == STREAM_STATE_RTP)
		{
			printf("start rtp\r\n");
		}
		else if (stream == STREAM_STATE_STOP)
		{
			printf("do nothing\r\n");
		}
		break;
	case STREAM_STATE_RTMP: //rtmping
		if (stream == STREAM_STATE_STOP)
		{
			printf("stop rtmp\r\n");
			stoprtmp();
		}
		else if (stream == STREAM_STATE_RTMP)
		{
			printf("check if rtmp url changed\r\n");
			/* code */
		}
		else if (stream == STREAM_STATE_RTP)
		{
			printf("stop rtmp,start rtp\r\n");
		}

		break;
	case STREAM_STATE_RTP: //rtping
		break;
	}


	return 0;
}
int down_event_process(char *msg)
{
	return 0;
}
//这里应该知己传入json串，然后区分MQTT心跳应答 还是媒体事件
void state_process(char *from, char *msg)
{

	int msgcode = getmsgcode(msg);

	printf("msgcode = %d \r\n", msgcode);

	if (msgcode == -1)
		return;

	if (msgcode == MSGCODE_HEARTBEAT_RTN)
	{ //处理心跳应答json

		heartbeat_rtn_process(msg);
	}
	else if (msgcode == MSGCOD_DOWN_EVENT)
	{ //处理主动下发的媒体参数变更事件
		down_event_process(msg);
	}

	return;

	char *url;
	int stream, storage;
	printf("file = %s,line = %d,url = %s,stream = %d,storage = %d\r\n", __FILE__, __LINE__, url, stream, storage);
	int state = rt_data.stream_state;

	switch (state)
	{
	case STREAM_STATE_STOP: //stopping
		if (stream == STREAM_STATE_RTMP)
		{ //应该先释放一次资源
			//stoprtmp();
			printf("start rtmp\r\n");
			startrtmp(url);
			//rt_data.stream_state=1;
		}
		else if (stream == STREAM_STATE_RTP)
		{
			printf("start rtp\r\n");
		}
		else if (stream == STREAM_STATE_STOP)
		{
			printf("do nothing\r\n");
		}
		break;
	case STREAM_STATE_RTMP: //rtmping
		if (stream == STREAM_STATE_STOP)
		{
			printf("stop rtmp\r\n");
			stoprtmp();
		}
		else if (stream == STREAM_STATE_RTMP)
		{
			printf("check if rtmp url changed\r\n");
			/* code */
		}
		else if (stream == STREAM_STATE_RTP)
		{
			printf("stop rtmp,start rtp\r\n");
		}

		break;
	case STREAM_STATE_RTP: //rtping
		break;
	}
}

int main(int argc, char **argv)
{

	int ret;
	struct MEDIA_WRITE mwrtp, mwrtmp, mwrtsp;

	readInifile();

	//创建异步操作
	create_consumer();

	//读取配置文件
	readcfg();

	//初始化运行参数
	get_runtime_initvalue();

	//启动串口2
	spu2 = new SerialPortU2();
	spu2->start();

	//根据配置模式来设置网络配置
	switch (networkmod)
	{
	case NETWORK_WIFIAP:
		wmgr = new WIFIMGR();
		wmgr->hostapd_ethdhcpd(0, 0);
		break;
	case NETWORK_4GLTE:
		nmgr = new NetWorkMgr();
		nmgr->start();
		break;
	case NETWORK_WIFISTA:
		wmgr = new WIFIMGR();
		wmgr->sta_eth(wifissid, wifipass);
		break;
	default:
		break;
	}

	//创建USB编码通道
	//irchn = new IRCHANNEL();

	//初始化TS会话
	memset(&ts_session, 0x0, sizeof(ts_session));
	ts_session.writets = writetsdata;

	//启动MQTT
	printf("start mqtt\n");
	set_mqtt_clientid(DEVID);
	set_p2psignal_callback(mqttArrived_Fun);
	start_mqtt_client();
	MQTTPROCESS *mqttprocess = new MQTTPROCESS();
	mqttprocess->start();

	

	//初始化音视频编码
	initavenc();

	initrtmp();

	device_state_mng(state_process);

	for (;;)
	{
		//打算在这里更新运行数据到rt_data结构
		printf("..........................................get status %d\r\n", rtmp.getrun_status());
		if (rtmp.getrun_status() == 1)
			rt_data.stream_state = 1;
		else
			rt_data.stream_state = 0;
		sleep(1);
	}

	if (mediamod == MEDIA_RTSP)
	{
		//设置rtsp码流回调函数和通道
		mwrtsp.write_a = rtsp_write_a;
		mwrtsp.write_v = rtsp_write_v;
		mwrtsp.chn = 0; //使用通道0的1080p码流
		register_mw(&mwrtsp);

		pause_write(1);
		av_start();
		rtspSrv = new RTSPServer();
		rtspSrv->start();
		rtmp.startPush();
		pause_write(0);

		for (;;)
		{

			sleep(1);
		}
	}

	if (mediamod == MEDIA_RTMP)
	{

		/*
		mwrtmp.write_a = rtmp_write_a;
		mwrtmp.write_v = rtmp_write_v;
		mwrtmp.chn = 1; //使用通道1的560p码流
		register_mw(&mwrtmp);

		rtmp.setUrl(rtmpurl);
		rtmp.start();

		pause_write(1);
		av_start();
		rtmp.startPush();
		pause_write(0);
		*/
		for (;;)
		{

			sleep(1);
		}
	}

	if (mediamod == MEDIA_AVCHAT)
	{
		int __port;
		sscanf(rtpport, "%d", &__port);
		mrtp = new rtp(rtphost,
					   __port,
					   44100,
					   (double)(1.0 / 90000.0));

		printf("start MEDIA_AVCHAT rtp %s:%d\n", rtphost, __port);

		mrtp->setPayloadtype(encode_mp2t);

		mwrtp.write_a = rtpts_write_a;
		mwrtp.write_v = rtpts_write_v;
		mwrtp.chn = 1; //使用通道1的560p码流
		register_mw(&mwrtp);

		pause_write(1);
		av_start();
		pause_write(0);

		for (;;)
		{
			mrtp->readloop();
		}
	}

	return 0;
}

//

#include "hi_include/watchdog.h"
#define DEV_FILE "/dev/watchdog"

void set_wd(int timeout)
{
	int ret;
	int fd = open(DEV_FILE, O_RDWR);
	if (fd < 0)
	{
		fprintf(stderr, "fail to open file:%s\n", DEV_FILE);
		goto err;
	}
	ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
	if (0 != ret)
	{
		fprintf(stderr, "[Error]%s(%d): ioctl ret =%d\n", __FUNCTION__, __LINE__, ret);
		close(fd);
		goto err;
	}

err:
	close(fd);
}

void feed_wd(int timeout)
{
	int ret;
	int fd = open(DEV_FILE, O_RDWR);
	if (fd < 0)
	{
		fprintf(stderr, "fail to open file:%s\n", DEV_FILE);
		goto err;
	}
	ret = ioctl(fd, WDIOC_KEEPALIVE);
	if (0 != ret)
	{
		fprintf(stderr, "[Error]%s(%d): ioctl ret =%d\n", __FUNCTION__, __LINE__, ret);
	}
err:
	close(fd);
}

int get_wd()
{
	int ret;
	int timeout;
	int fd = open(DEV_FILE, O_RDWR);
	ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
	if (0 != ret)
	{
		fprintf(stderr, "[Error]%s(%d): ioctl ret =%d\n", __FUNCTION__, __LINE__, ret);
		goto err;
	}
	else
	{
		fprintf(stdout, "Resp: timeout=%d\n", timeout);
	}
err:
	close(fd);

	return timeout;
}
